#ifndef RAY_HPP
#define RAY_HPP

#include "tvector.hpp"
#include <iostream>

/**
 * @brief The simple ray class representation : r(t) = o + dt  t in [0, tmax)
 * @note
 * @tparam T data type
 */
template <typename T>
class ray {
public:
  ray() : tmax( 0 ), time( 0 ), isInit(false) {
    _ray[0] = tvec<T, 3>(), _ray[1] = tvec<T, 3>();
  }
  ray( tvec<T, 3> &o, tvec<T, 3> &d ) : isInit(false) {
    _ray[0] = o, _ray[1] = d, tmax = T( 0 ), time = T( 0 );
  }
  ray( const ray &r ) { *this = r; }

  tvec<T, 3> origin() const { return _ray[0]; }
  tvec<T, 3> direction() const { return _ray[1]; }

  /**
   * @brief  convience for getting parameter t
   * @note
   * @param t: the parameter
   * @retval
   */
  inline tvec<T, 3> operator()( T t ) {
    if ( !isInit ) {
      isInit = true;
      _cached = _ray[0] + _ray[1] * t;
      _t = t;
    }
    if ( _t != t ) {
      _cached = _ray[0] + _ray[1] * t;
      _t = t;
    }
    return _cached;
  }

  inline tvec<T, 3> operator()( T t ) const {
    if ( !isInit ) {
      isInit = true;
      _cached = _ray[0] + _ray[1] * t;
      _t = t;
    }
    if ( _t != t ) {
      _cached = _ray[0] + _ray[1] * t;
      _t = t;
    }
    return _cached;
  }

#pragma region ray data member

  tvec<T, 3> _ray[2]; // the first is origin, the second is direction
  mutable T tmax;     // the max parameter t
  f32 time;
#pragma endregion

private:
  mutable tvec<T, 3> _cached;
  mutable float _t;
  mutable bool isInit;
};

#include "ray_operator.inl"

using Ray = ray<f32>;

#endif